package top.xana.acg.anime

import android.Manifest
import android.app.UiModeManager
import android.bluetooth.BluetoothAdapter
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Outline
import android.media.AudioManager
import android.net.wifi.WifiManager
import android.os.Bundle
import android.os.Handler
import android.view.KeyEvent
import android.view.View
import android.view.ViewOutlineProvider
import android.widget.ImageView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.permissionx.guolindev.PermissionX
import dalvik.system.DexClassLoader
import top.xana.acg.anime.utils.LogE
import top.xana.acg.anime.utils.ToastUtils
import top.xana.acg.anime.utils.dp
import top.xana.acg.reflect.FieldUtils
import top.xana.acg.reflect.MethodUtils
import java.io.File
import java.lang.reflect.Proxy

class TestActivity : AppCompatActivity() {

    private val launcher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
        doLoad()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test)
        if (!Utils.isNotificationListenerEnabled(this)) {//是否开启通知使用权
            Utils.openNotificationListenSettings(this)
        }

    }

    override fun onRestart() {
        super.onRestart();
        if (Utils.isNotificationListenerEnabled(this)) {//开启通知使用权后,重启服务再走一下onStartCommand方法,使设置有效
            startService(Intent(this, MediaControllerService::class.java))
        }
    }

    private val audioManager by lazy {
        ContextCompat.getSystemService(
            this,
            AudioManager::class.java
        )
    }

    private val uiModeManager by lazy {
        ContextCompat.getSystemService(
            this,
            UiModeManager::class.java
        )
    }

    private val wifiManager by lazy {
        ContextCompat.getSystemService(
            this,
            WifiManager::class.java
        )
    }

    fun click(view: View) {
        when (view.id) {
            R.id.btnLast -> doLast()
            R.id.btnPlay -> doPlayOrPause()
            R.id.btnNext -> doNext()
            R.id.btnTheme -> doTheme()
            R.id.btnWifi -> doWifi()
            R.id.btnBluetooth -> doBluetooth()
            R.id.btnRemote -> doLoad()
            R.id.btnHookAm -> doHook()
            R.id.btnToPlugin -> doJump()
            else -> doLayerCard(view)

        }
    }

    private fun doHook() {
        hookAtm()
        hookH()
    }

    private fun hookAtm() {
        val className = "android.app.ActivityTaskManager"
        val singletonField = FieldUtils.getDeclaredField(className, "IActivityTaskManagerSingleton", true)
        val singleton = singletonField.get(null)
        val singletonClazz = singleton.javaClass.superclass
        val singleGetMethod = MethodUtils.getAccessibleMethod(singletonClazz, "get")
        val singleInstanceField = FieldUtils.getDeclaredField(singletonClazz, "mInstance", true)
        val atm = singleGetMethod.invoke(singleton)!!
        val atmProxy = createProxy(atm)
        singleInstanceField.set(singleton, atmProxy)
    }
    private fun createProxy(atm: Any) = Proxy.newProxyInstance(
        atm.javaClass.classLoader,
        atm.javaClass.interfaces
    ) { proxy, method, args ->
        if(method.name == "startActivity") {
            LogE("startActivity... start", TAG)
            val p = method.parameterTypes
            args.forEachIndexed { i, a ->
                LogE("${p.getOrNull(i)?.simpleName}: $a", TAG)
                if(a is Intent) {
                    val component = a.component
                    a.putExtra(KEY_COMPONENT, component)
                    a.component = ComponentName(component!!.packageName, TempActivity::class.java.name)
                }
            }
            val ret = method.invoke(atm, *args)
            LogE("startActivity... end", TAG)
            ret
        } else {
            method.invoke(atm, *args)
        }
    }

    private fun hookH() {
        val activityThreadClz = Class.forName("android.app.ActivityThread")
        val activityThreadMethod = MethodUtils.getAccessibleMethod(activityThreadClz, "currentActivityThread")
        val activityThread = activityThreadMethod.invoke(null)
        val handler = getHandler(activityThreadClz, activityThread)
        val hCallbackField = FieldUtils.getDeclaredField(Handler::class.java, "mCallback", true)
        val hCallback = hCallbackField.get(handler) as? Handler.Callback
        val launchActivityItemClazz = Class.forName("android.app.servertransaction.LaunchActivityItem")
        val intentField = FieldUtils.getDeclaredField(launchActivityItemClazz, "mIntent", true)
        val hookedCallback = Handler.Callback {
            LogE("${it.what}, ${it.obj}", tag = TAG)
            if(it.what == 159) {
                val transaction = it.obj
                val callbacks = MethodUtils.getAccessibleMethod(transaction.javaClass, "getCallbacks").invoke(transaction) as List<*>
                for(cb in callbacks) {
                    LogE("callback: $cb", tag = TAG)
                    if(launchActivityItemClazz.isAssignableFrom(cb?.javaClass)) {
                        val intent = intentField.get(cb) as Intent
                        val originalComponent = intent.getParcelableExtra<ComponentName>(KEY_COMPONENT)
                        LogE("originalComponent: $originalComponent", TAG)
                        if(originalComponent != null) {
                            intent.component = originalComponent
                            break
                        }
                    }
                }
            }
            hCallback?.handleMessage(it)?: false
        }
        hCallbackField.set(handler, hookedCallback)
    }

    private fun getHandler(clz: Class<*>, obj: Any): Handler {
        val field = FieldUtils.getDeclaredField(clz, "mH", true)
        var handler = field.get(obj)
        if(null == handler) {
            val getHandlerMethod = MethodUtils.getAccessibleMethod(clz, "getHandler")
            val activityThread = getHandlerMethod.invoke(obj)
            handler = getHandlerMethod.invoke(activityThread)
        }
        return handler as Handler
    }

    private fun doLoad() {
        val granted0 = checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)==PERMISSION_GRANTED
        val granted1 = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)==PERMISSION_GRANTED
        if(!(granted0 && granted1)) {
            launcher.launch(
                arrayOf(
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,
                )
            )
            return
        }
        val aarFile = File(this.filesDir, "plugin-release.aar")
        LogE("aar is exist: ${aarFile.exists()}\n$aarFile", TAG)
        val dexClassLoader = DexClassLoader(aarFile.absolutePath, cacheDir.absolutePath, null, classLoader)
        LogE("dexClassloader: $dexClassLoader", TAG)
        val pluginClassName = "top.xana.exp.plugin.PluginActivity"
//        val clazz = dexClassLoader.loadClass(pluginClassName)
//        LogE("plugin class: $clazz", TAG)
        loadPlugin(this, aarFile.absolutePath)
        val clazz = Class.forName(pluginClassName)
        LogE("plugin class: $clazz", TAG)
    }

    private fun loadPlugin(context: Context, path: String) {
        /**
         * 宿主dexElements = 宿主dexElements ＋ 插件dexElements
         * 1.获取宿主dexElements
         * 2.获取插件dexElements
         * 3.合并两个dexElements
         * 4.将新的dexElements赋值到宿主dexElements
         */

        val dexClassLoaderClass = Class.forName("dalvik.system.BaseDexClassLoader")
        val pathListField = FieldUtils.getDeclaredField(dexClassLoaderClass, "pathList", true)

        val dexPathListClass = Class.forName("dalvik.system.DexPathList")
        val dexElementsField = FieldUtils.getDeclaredField(dexPathListClass, "dexElements", true)

        val hostClassLoader = context.classLoader
        val hostPathList = pathListField.get(hostClassLoader)
        val hostDexElements = dexElementsField.get(hostPathList) as Array<*>
        val dexClassLoader = DexClassLoader(
            path,
            context.cacheDir.absolutePath,
            null,
            hostClassLoader
        )
        val pluginPathList = pathListField.get(dexClassLoader)
        val pluginDexElements = dexElementsField.get(pluginPathList) as Array<*>
        val newDexElements = java.lang.reflect.Array.newInstance(
            hostDexElements.javaClass.componentType,
            hostDexElements.size + pluginDexElements.size
        ) as Array<*>
        System.arraycopy(hostDexElements, 0, newDexElements, 0, hostDexElements.size)
        System.arraycopy(
            pluginDexElements,
            0,
            newDexElements,
            hostDexElements.size,
            pluginDexElements.size
        )
        dexElementsField.set(hostPathList, newDexElements)
    }


    private fun doJump() {
        val intent = Intent(this, TargetActivity::class.java)
        startActivity(intent)
    }

    private fun doRemote() {
        val intent = Intent(this, RemoteActivity::class.java)
        startActivity(intent)
    }

    private fun doLast() {
        val downEv = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PREVIOUS)
        val upEv = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PREVIOUS)
        audioManager?.dispatchMediaKeyEvent(downEv)
        audioManager?.dispatchMediaKeyEvent(upEv)
    }

    private fun doNext() {
        val downEv = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT)
        val upEv = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT)
        audioManager?.dispatchMediaKeyEvent(downEv)
        audioManager?.dispatchMediaKeyEvent(upEv)
    }

    private fun doPlayOrPause() {
        val downEv = KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE)
        val upEv = KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE)
        audioManager?.dispatchMediaKeyEvent(downEv)
        audioManager?.dispatchMediaKeyEvent(upEv)
        toast("${audioManager?.isMusicActive}")
    }

    private fun doTheme() {
        val mode = uiModeManager?.nightMode
        when (mode) {
            UiModeManager.MODE_NIGHT_YES -> toast("深色模式")
            UiModeManager.MODE_NIGHT_NO -> toast("浅色模式")
            UiModeManager.MODE_NIGHT_AUTO -> toast("自动模式")
            UiModeManager.MODE_NIGHT_CUSTOM -> toast("定义模式")
        }
    }

    private fun doWifi() {
        val f = wifiManager?.isWifiEnabled ?: false
        wifiManager?.isWifiEnabled = !f
    }

    private fun doBluetooth() {
        val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)

        PermissionX.init(this)
            .permissions(Manifest.permission.BLUETOOTH_CONNECT)
            .request { granted, _, _ ->
                if(granted) {

                }
            }

        startActivity(intent)
    }

    private fun doLayerCard(v: View) {
        val constructor = Main::class.java.getDeclaredConstructor(String::class.java, BooleanArray::class.java)
        LogE("$constructor", tag = "Lance")
        v.setRoundCorner(12f.dp)
        val width = 100.dp
        val height = 100.dp
        val mode = View.MeasureSpec.EXACTLY
        val widthSpec = View.MeasureSpec.makeMeasureSpec(width, mode)
        val heightSpec = View.MeasureSpec.makeMeasureSpec(height, mode)
        v.measure(widthSpec, heightSpec)
        v.layout(0, 0, width, height)
        val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        val canvas = Canvas(bmp)
        v.draw(canvas)
        findViewById<ImageView>(R.id.img)?.setImageBitmap(bmp)
    }

    private fun toast(msg: String) = ToastUtils.showShort(msg)


    fun View.setRoundCorner(roundSize: Float) {
        outlineProvider = object : ViewOutlineProvider() {
            override fun getOutline(view: View?, outline: Outline?) {
                view ?: return
                outline?.setRoundRect(0, 0, view.width, view.height, roundSize)
            }
        }
        clipToOutline = true
    }

    companion object {
        const val TAG = "TestActivity"

        const val KEY_COMPONENT = "component"
    }
}